package com.boco.fc.auto.deploy;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import java.util.Scanner;

import org.apache.log4j.Logger;

public class Deploy {
	
	public Logger log = Logger.getLogger(this.getClass());
	
	private String deployDateString ;
	
	// 上传文件数
	private int uploadCount = 0;

	// 新增文件数
	private int uploadNewCount = 0;

	// 现在文件数
	private int downloadCount = 0;

	// 目录创建数
	private int mkdirCount = 0;

	// 执行命令数
	private int exeCommandCount = 0;

	// 开始处理时间
	private long startTime = 0L;

	// 处理结束时间
	private long endTime = 0L;
	
	// 恢复数据集合
	private List<String> recoverList ;
	
	public static Deploy getInstance(){
		return new Deploy();
	}
	
	/**
	 * start Deploy
	 * @param xmlPath
	 */
	public void start(String xmlPath) throws Exception {
		log.info("发布开始.............");
		//初始化发布时间
		deployDateString = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
		
		XmlUtil xmlUtil = new XmlUtil();
		//执行发布
		//List<Map<String, Object>> serverList = xmlUtil.parse(xmlPath);
		List<Map<String, Object>> serverList = xmlUtil.parseJdom(xmlPath);
		//验证本地发布配置
		this.uploadFileCheck(serverList);
		//验证远程发布配置
		this.uploadRemoteCheck(serverList);
		//执行发布
		this.executeDeploy(serverList);
		log.info("发布结束。");
	}
	
	/**
	 * 检查uploads文件配置
	 * @param serverList
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private void uploadFileCheck(List<Map<String, Object>> serverList) {
		boolean check = true;
		for(Map<String, Object> server:serverList){
			log.info("检查uploads配置："+server.get("host"));
			List<Map<String, String>> uploadsList = (List<Map<String, String>>)server.get("uploads");
			for (Map<String, String> upload : uploadsList) {
				String fileMode = upload.get("fileMode");
				String localFile = upload.get("localFile");
				String localPath = upload.get("localPath");
				String localRoot = upload.get("localRoot");
				File file = new File(localRoot + localPath + localFile);
				if(!file.exists()){
					if("file".equals(fileMode)){
						log.error("本地文件不存在："+file.getAbsolutePath());
					}else if("dir".equals(fileMode)){
						log.error("本地目录不存在："+file.getAbsolutePath());
					}
					check = false;
				}else{
					if("file".equals(fileMode) && !file.isFile()){
						log.error("不是文件："+file.getAbsolutePath());
						check = false;
					}else if("dir".equals(fileMode) && !file.isDirectory()){
						log.error("不是目录："+file.getAbsolutePath());
						check = false;
					}
				}
			}
		}
		if(!check){
			log.info("本地发布配置检查未通过，发布结束。");
			System.exit(0);
		}
	}
	
	/**
	 * 检查远程发布路径配置
	 * @param serverList
	 * @return
	 */
	@SuppressWarnings({ "unchecked"})
	private void uploadRemoteCheck(List<Map<String, Object>> serverList) throws Exception {
		boolean check = true;
		for(Map<String, Object> server:serverList){
			log.info("检查remotePath配置："+server.get("host"));
			SSH2Util sshUtil = new SSH2Util(server);
			List<Map<String, String>> uploadsList = (List<Map<String, String>>)server.get("uploads");
			for (Map<String, String> upload : uploadsList) {
				String remotePath = upload.get("remotePath");
				String remoteFile = upload.get("remotePath") + upload.get("localFile");
				if((!sshUtil.exists(remotePath) || !sshUtil.exists(remoteFile)) && check){
					check = false;
				}
			}
			sshUtil.closeConnection();//关闭远程连接
		}
		if(!check){
			Scanner s = new Scanner(System.in);
			log.info("远程发布路径不存在,是否创建(yes/no)："); 
            String line = s.nextLine();
            if ("YES".equals(line.toUpperCase()) || "Y".equals(line.toUpperCase())){
            	check = true;
            	log.info("自动创建远程文件或者目录：YES");
            }else{
            	check = false;
            }
		}
		if(!check){
			log.info("远程发布配置检查未通过，发布结束。");
			System.exit(0);
		}
	}
	

	/**
	 * executeDeploy
	 * @param serverList
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private void executeDeploy(List<Map<String, Object>> serverList) throws Exception {
		for(Map<String, Object> server:serverList){
			log.debug(server.toString());
			//
			this.initCount();
			//连接ssh
			SSH2Util sshUtil = new SSH2Util(server);
			//定义备份目录
			String backupPath = "backup" + File.separator + deployDateString + File.separator + server.get("host");
			//根据uploads配置发布文件
			List<Map<String, String>> uploadsList = (List<Map<String, String>>)server.get("uploads");
			for (Map<String, String> upload : uploadsList) {
			    this.upload(sshUtil,upload,backupPath);
			}
			//根据commands配置执行命令
			List<String> commandsList = (List<String>) server.get("commands");
			for (String command : commandsList) {
			      if(command!=null){
			    	  sshUtil.execCommand(command, true);
			    	  this.exeCommandCount += 1; //
			      }
			}
			sshUtil.closeConnection();//关闭远程连接
			this.saveRecoverFile((String)server.get("host"));
			this.dealResult((String) server.get("host"));//打印发布情况
		}
	}
	
	/**
	 * @throws Exception 
	 * 保存恢复记录内容
	 */
	private void saveRecoverFile(String serverIp) throws Exception {
		log.info("保存恢复文件");
		File recoverDir = new File("recover"+File.separator+deployDateString);
		File recoverFile = new File("recover"+File.separator+deployDateString+File.separator+serverIp+".rcv");
		recoverDir.mkdirs();
		recoverFile.createNewFile();
		log.info(recoverFile.getAbsolutePath());
		log.info("恢复命令：" + "sh recover.sh deploy/config.xml " + "recover"+File.separator + deployDateString + File.separator+serverIp+".rcv");
        PrintStream ps = new PrintStream(new FileOutputStream(recoverFile));
        for (String recover : this.recoverList) {
        	ps.append(recover+"\n");
        }
        ps.close();
	}

	/**
	 * 备份、上传文件
	 * @param sshUtil
	 * @param upload
	 * @param backupPath
	 */
	private void upload(SSH2Util sshUtil, Map<String, String> upload, String backupPath) throws Exception { 
		log.debug(upload.toString());
		
		String fileMode = upload.get("fileMode");
		String localFile = upload.get("localFile");
		String localPath = upload.get("localPath");
		String localRoot = upload.get("localRoot");
		String remotePath = upload.get("remotePath");
		
		if(fileMode!=null && fileMode.equals("file")){
			if (sshUtil.exists(remotePath + localPath + localFile)) {
				sshUtil.downloadFile(remotePath + localPath + localFile,  localRoot + backupPath + File.separator + localPath);//备份远程文件
				this.downloadCount += 1; //
				sshUtil.uploadFile(localRoot + localPath + localFile,  remotePath + localPath);//上传本地文件
				this.uploadCount += 1; //
				recoverList.add("update|" + remotePath + localPath + "|" + localRoot + backupPath + File.separator + localPath + localFile);
			}else{
				sshUtil.uploadFile(localRoot + localPath + localFile,  remotePath + localPath);//上传本地文件
				this.uploadNewCount += 1; //
				recoverList.add("add|" + remotePath + localPath + localFile);
			}
		}else if(fileMode!=null && fileMode.equals("dir")){
			if (!sshUtil.exists(remotePath + localPath + localFile)) {// 先判断目录是否存在
				sshUtil.mkdirCmd(remotePath + localPath + localFile); //在远程创建文件夹
				this.mkdirCount += 1;//
				recoverList.add("mkdir|" + remotePath + localPath + localFile);
			}
			//获取目录文件，循环处理
			File[] files = new File(localRoot + localPath + localFile).listFiles();
			if(files != null && files.length > 0){
				for(File f : files){
					//组织upload参数
					Map<String, String> _upload = new HashMap<String, String>();
					_upload.put("fileMode",   f.isFile()?"file":"dir");
					_upload.put("localFile",  f.getName());
					_upload.put("localPath",  f.getParent().replaceAll(localRoot, "")+File.separator);
					_upload.put("localRoot",  localRoot);
					_upload.put("remotePath", remotePath);
					this.upload(sshUtil, _upload, backupPath);//递归
				}
			}
		}
	}
	
	/**
	 * 初始化发布计数
	 */
	private void initCount() {
		this.uploadCount = 0; // 上传文件数
		this.uploadNewCount = 0; // 新增文件数
		this.downloadCount = 0; // 现在文件数
		this.mkdirCount = 0; // 目录创建数
		this.exeCommandCount = 0; // 执行命令数
		this.startTime = System.currentTimeMillis();
		this.recoverList = new ArrayList<String>();
	}
	
	/**
	 * 打印远程处理情况日志
	 */
	private void dealResult(String serverIp) {
		this.endTime = System.currentTimeMillis();
		log.info("==========================================================================");
		log.info("在" + serverIp + "上发布完成，用时:" + (this.endTime - this.startTime) / 1000 + " 秒");
		log.info("创建目录：" + this.mkdirCount + " 个");
		log.info("上传文件：" + this.uploadCount + " 个");
		log.info("下载文件：" + this.downloadCount + " 个");
		log.info("新增文件：" + (this.uploadNewCount - this.mkdirCount) + " 个");
		log.info("执行命令：" + this.exeCommandCount + " 个");
		log.info("恢复命令：" + "sh recover.sh deploy/config.xml " + "recover"+File.separator + deployDateString);
		log.info("==========================================================================");
	}
	
}
